/*
 *  Copyright 2016 Google Inc. All Rights Reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.google.blockly.ohos;

import com.google.blockly.ohos.control.BlocklyController;
import com.google.blockly.ohos.control.FlyoutController;
import com.google.blockly.ohos.ui.BlockListUI;
import com.google.blockly.ohos.ui.BlockRecyclerViewHelper;
import com.google.blockly.ohos.ui.CategorySelectorUI;
import com.google.blockly.ohos.ui.FlyoutCallback;
import com.google.blockly.ohos.ui.WorkspaceHelper;
import com.google.blockly.model.Block;
import com.google.blockly.model.BlocklyCategory;
import com.google.blockly.utils.ColorUtils;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;

/**
 * A drawer UI to show a set of {@link Block Blocks} one can drag into the workspace. The
 * available blocks are provided by a {@link BlocklyCategory}, with this Fraction
 * displaying a single set of blocks. Set the blocks currently being shown by using
 * {@link #setCurrentCategory(BlocklyCategory)}.
 * <p/>
 * This Fraction is often used with {@link CategorySelectorFraction} which displays a list of tabs
 * built from a root category. The Fractions don't interact directly, but the
 * {@link FlyoutController} can be used to handle the interaction between these components.
 * <p/>
 * The behavior of the {@code FlyoutFraction} is configurable in xml. {@code closeable} and
 * {@code scrollOrientation} attributes may be set to affect the display and behavior of this
 * Fraction.
 * When {@code blockly:closeable} is true, the drawer of blocks will hide in the closed state. The
 * tabs will remain visible, providing the user a way to open the drawers.
 * <p/>
 * {@code blockly:scrollOrientation} can be either {@code horizontal} or {@code vertical}, and
 * affects only the block list. The tab scroll orientation is determined by the {@code tabEdge}.
 * <p/>
 */
// TODO(#9): Attribute and arguments to set the tab background.
public class FlyoutFraction extends Fraction implements BlockListUI {
    private static final String TAG = "FlyoutFraction";

    public static final int DEFAULT_BLOCKS_BACKGROUND_ALPHA = 0xBB;
    public static final int DEFAULT_BLOCKS_BACKGROUND_COLOR = Color.LTGRAY.getValue();
    protected static final float BLOCKS_BACKGROUND_LIGHTNESS = 0.75f;

    protected int mBgAlpha = DEFAULT_BLOCKS_BACKGROUND_ALPHA;
    protected int mBgColor = DEFAULT_BLOCKS_BACKGROUND_COLOR;

    protected Component mFlyoutView;
    protected BlocklyController mController;
    protected WorkspaceHelper mHelper;

    protected boolean mCloseable;
    protected int mScrollOrientation;

    protected FlyoutCallback mViewCallback = null;
    protected BlockRecyclerViewHelper mRecyclerHelper;
    protected Color mBgDrawable = new Color(mBgColor);

    public FlyoutFraction(int scrollOrientation, boolean closeable) {
        mScrollOrientation = scrollOrientation;
        mCloseable = closeable;
    }

    protected Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
        // Read configuration, preferring the saved values over the inflated values

        int layout = mScrollOrientation == Component.VERTICAL
                ? ResourceTable.Layout_default_flyout_start : ResourceTable.Layout_default_flyout_bottom;

        mFlyoutView = scatter.parse(layout, container, false);

        ListContainer recyclerView = (ListContainer) mFlyoutView.findComponentById(ResourceTable.Id_block_list_view);
        mRecyclerHelper = new BlockRecyclerViewHelper(recyclerView, mFlyoutView.getContext());
        mRecyclerHelper.setScrollOrientation(mScrollOrientation);
        return mFlyoutView;
    }


    /**
     * Connects the {@link FlyoutFraction} to the application's drag and click handling. It is
     * called by
     * {@link BlocklyController#setToolboxUi(BlockListUI, CategorySelectorUI)}
     * and should not be called by the application developer.
     *
     * @param callback The callback that will handle user actions in the flyout.
     */
    public void init(BlocklyController controller, FlyoutCallback callback) {
        if (mController != null) {
            throw new IllegalStateException("This flyout is already initialized!");
        }
        mController = controller;
        mViewCallback = callback;
        if (mRecyclerHelper == null) {
            return;
        }
        if (mController == null) {
            mRecyclerHelper.reset();
            return;
        }
        mRecyclerHelper.init(mController, callback);
    }

    /**
     * Sets the Flyout's current {@link BlocklyCategory}, including opening or closing the drawer.
     * In closeable toolboxes, {@code null} {@code category} is equivalent to closing the drawer.
     * Otherwise, the drawer will be rendered empty.
     *
     * @param category The {@link BlocklyCategory} with blocks to display.
     */
    public void setCurrentCategory(BlocklyCategory category) {
        mRecyclerHelper.setCurrentCategory(category);
        updateCategoryColors(category);
        // TODO(#80): Add animation hooks for subclasses.
        if (category == null) {
            mFlyoutView.setVisibility(Component.INVISIBLE);
        } else {
            mFlyoutView.setVisibility(Component.VISIBLE);
        }
    }

    /**
     * @return The currently set category.
     */
    public BlocklyCategory getCurrentCategory() {
        return mRecyclerHelper.getCurrentCategory();
    }

    /**
     * Sets whether this flyout should be allowed to close.
     *
     * @param closeable True to allow it to close, false otherwise.
     */
    public void setCloseable(boolean closeable) {
        mCloseable = closeable;
    }

    /**
     * @return True if this flyout is allowed to close, false otherwise.
     */
    public boolean isCloseable() {
        return mCloseable;
    }

    /**
     * @return True if this Fraction is currently visible, false otherwise.
     */
    public boolean isOpen() {
        return mFlyoutView.getVisibility() == Component.VISIBLE;
    }

    /**
     * Attempts to close the blocks drawer.
     *
     * @return True if an action was taken (the drawer is closeable and was previously open).
     */
    public boolean closeUi() {
        if (!isCloseable() || mFlyoutView.getVisibility() == Component.INVISIBLE) {
            return false;
        }
        mRecyclerHelper.setCurrentCategory(null);
        mFlyoutView.setVisibility(Component.INVISIBLE);
        updateCategoryColors(null);
        return true;
    }

    /**
     * Sets the background color for the flyout's view based on the category.
     *
     * @param curCategory The category to set the color from or null.
     */
    protected void updateCategoryColors(BlocklyCategory curCategory) {
        Integer maybeColor = curCategory == null ? null : curCategory.getColor();
        int bgColor = mBgColor;
        if (maybeColor != null) {
            bgColor = getBackgroundColor(maybeColor);
        }
        int[] rgb = To_RGB(bgColor);
        ShapeElement element = new ShapeElement();
        element.setRgbColor(new RgbColor(rgb[0], rgb[1], rgb[2]));
        element.setAlpha(mBgAlpha);
        mFlyoutView.setBackground(element);
    }

    private int[] To_RGB(int color) {

        int red = (color & 0xff0000) >> 16;
        int green = (color & 0x00ff00) >> 8;
        int blue = (color & 0x0000ff);
        return new int[]{red, green, blue};
    }

    /**
     * Adjusts a color to make it appropriate for a background behind blocks of that color.
     *
     * @param categoryColor The color to adjust for the background.
     * @return A color appropriate for the background behind blocks.
     */
    protected int getBackgroundColor(int categoryColor) {
        return ColorUtils.blendRGB(categoryColor, Color.WHITE.getValue(), BLOCKS_BACKGROUND_LIGHTNESS);
    }
}
